﻿using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Trippy.Camera;
using Trippy.CollidableGameComponents;
using Trippy.CollidableGameComponents.Events;
using Trippy.CollidableGameComponents.Objects;
using Microsoft.Xna.Framework.Input;
using Trippy.Effects;
using Trippy.CollidableGameComponents.Agents;
using Trippy.Abilities;
using Trippy.UI_Overlay;
using Color = Microsoft.Xna.Framework.Color;
using Rectangle = Microsoft.Xna.Framework.Rectangle;

namespace Trippy.Levels
{
    public class Level : TrippyState
    {
        public TrippyCamera Camera { get; protected set; }
        public Texture2D Background { get; protected set; }
        public UIOverlay Overlay { get; protected set; }
        public Color UIColor { get; protected set; }

        // Physics configuration
        public Vector2 Gravity { get; protected set; }
        public float Friction { get; protected set; } // 0f < f < 1f
        
        public RectangleF Bounds { get; protected set; }

        public Hero Hero { get; protected set; }
        public Vector2 SpawnPoint { get; set; }

        public new TrippyGame Game { get; protected set; }

        public GameComponentCollection Objects;
        public EventManager eventManager { get; protected set; }

        private SpacePartitioner spacePartitioner;

        private SpriteBatch spriteBatch;

        public List<CollidableGameComponent> powerList = new List<CollidableGameComponent>();

        public float LeftPadding { get; protected set; }
        public float RightPadding { get; protected set; }
        public float TopPadding { get; protected set; }
        public float BottomPadding { get; protected set; }

        public Level(TrippyGame game, Hero hero) : base(game)
        {
            Game = game;
            Hero = hero;
            Objects = new GameComponentCollection();
            Overlay = new UIOverlay(this);
        }

        public void Spawn(Hero hero)
        {
            hero.Position = SpawnPoint; 
            hero.Velocity = new Vector2();
        }

        public void UpdateBounds()
        {
            if (spacePartitioner != null) return;

            float minX = float.PositiveInfinity;
            float minY = float.PositiveInfinity;
            float maxX = float.NegativeInfinity;
            float maxY = float.NegativeInfinity;

            CollidableGameComponent[] collidableComponents = Objects.OfType<CollidableGameComponent>().ToArray();

            foreach (CollidableGameComponent c in collidableComponents)
            {
                if (c.BoundingBox.X < minX)
                {
                    minX = c.BoundingBox.X;
                }
                if (c.BoundingBox.X + c.BoundingBox.Width > maxX)
                {
                    maxX = c.BoundingBox.X + c.BoundingBox.Width;
                }
                if (c.BoundingBox.Y + c.BoundingBox.Height > maxY)
                {
                    maxY = c.BoundingBox.Y + c.BoundingBox.Height;
                }
                if (c.BoundingBox.Y < minY)
                {
                    minY = c.BoundingBox.Y;
                }
            }

            if (minX == 0f && minY == 0f && maxX == 0f && maxY == 0f)
            {
                return;
            }

            minX = minX - LeftPadding;
            minY = minY - TopPadding;
            float width = maxX - minX + LeftPadding + RightPadding;
            float height = maxY - minY + TopPadding + BottomPadding;

            Bounds = new RectangleF(minX, minY, width, height);

            spacePartitioner = new SpacePartitioner(minX, minY, minX + width, minY + height, 100f);
        }

        protected override void LoadContent()
        {

            base.LoadContent();
        }

        public void DetectCollisions()
        {
            if (spacePartitioner == null) return;

            CollidableGameComponent[] collidableComponents = Objects.OfType<CollidableGameComponent>().ToArray();

            foreach (CollidableGameComponent c in collidableComponents)
            {
                spacePartitioner.Add(c);
            }

            foreach (CollidableGameComponent c in collidableComponents)
            {
                var matches = spacePartitioner.FindNeighbors(c);
                foreach (CollidableGameComponent match in matches)
                {
                    c.DetectCollisions(match);
                }
            }

            spacePartitioner.ClearGrid();
        }

        public bool InBounds(DrawableTrippyComponent component)
        {
            if (spacePartitioner == null) return true;

            if (component.Position.X > Bounds.X && component.Position.X < Bounds.X + Bounds.Width && 
                component.Position.Y > Bounds.Y && component.Position.Y < Bounds.Y + Bounds.Height)
            {
                return true;
            }
            return false;
        }

        public virtual void Add(GameComponent x)
        {
            if (!Objects.Contains(x))
                Objects.Add(x);
        }

        public virtual void Remove(GameComponent x)
        {
            if (!(x is Trippy.CollidableGameComponents.Projectiles.Fireball)
            && !(x is Trippy.CollidableGameComponents.Projectiles.Missile)
            && !(x is Trippy.CollidableGameComponents.Projectiles.EnemyFireball)
            && !(x is Orb)
            && !(powerList.Contains(x)))
            {
                if(x is CollidableGameComponent)
                powerList.Add((CollidableGameComponent) x);
            }
            Objects.Remove(x);
        }

        public override void Initialize()
        {
            spriteBatch = new SpriteBatch(Game.GraphicsDevice);

            foreach (GameComponent gameComponent in Objects)
            {
                gameComponent.Initialize();
            }

            UpdateBounds();

            base.Initialize();
        }

        public override void Draw(GameTime gameTime)
        {
            spriteBatch.Begin();
            spriteBatch.Draw(Background,
                new Rectangle(0, 0, GraphicsDevice.Viewport.Width, GraphicsDevice.Viewport.Height),
                Color.White);
            spriteBatch.End();

            foreach (DrawableGameComponent drawableGameComponent in Objects.OfType<DrawableGameComponent>())
            {
                drawableGameComponent.Draw(gameTime);
            }
            base.Draw(gameTime);

            spriteBatch.Begin(SpriteSortMode.BackToFront, BlendState.AlphaBlend);
            Overlay.draw(Hero.Score, Hero.Lives, Hero.CurrentAbility, spriteBatch);
            spriteBatch.End();
        }

        public override void Update(GameTime gameTime)
        {
            UpdateBounds();

            DrawableTrippyComponent[] drawableComponents = Objects.OfType<DrawableTrippyComponent>().ToArray();

            foreach (DrawableTrippyComponent gameComponent in drawableComponents)
            {
                gameComponent.PreUpdate();
            }

            foreach (GameComponent gameComponent in Objects)
            {
                gameComponent.Update(gameTime);
            }

            base.Update(gameTime);
            DetectCollisions();

            foreach (DrawableTrippyComponent gameComponent in drawableComponents)
            {
                if (!InBounds(gameComponent))
                {
                    gameComponent.MarkForDeletion(Hero.MannerOfDeath.Falling);           
                }
                gameComponent.PostUpdate(TrippyUtilities.CalculateElapsedTime(gameTime));
            }
            
        }

        protected virtual void Generate()
        {

        }

        public void updateKeys()
        {
            PreviousKeyboardState = CurrentKeyboardState;
            CurrentKeyboardState = Keyboard.GetState();
        }
    }
}
